package com.brodong.animal_remake.common.entities.monster.realization;

import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.Mth;
import net.minecraft.world.Difficulty;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.*;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.ai.control.MoveControl;
import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal;
import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
import net.minecraft.world.entity.animal.IronGolem;
import net.minecraft.world.entity.monster.Ghast;
import net.minecraft.world.entity.monster.Monster;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.LargeFireball;
import net.minecraft.world.entity.raid.Raider;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import software.bernie.geckolib3.core.IAnimatable;
import software.bernie.geckolib3.core.PlayState;
import software.bernie.geckolib3.core.builder.AnimationBuilder;
import software.bernie.geckolib3.core.controller.AnimationController;
import software.bernie.geckolib3.core.event.predicate.AnimationEvent;
import software.bernie.geckolib3.core.manager.AnimationData;
import software.bernie.geckolib3.core.manager.AnimationFactory;

import java.util.EnumSet;
import java.util.Random;

public class FlyingRangeBasicEntity extends FlyingMob implements IAnimatable {
    private static final EntityDataAccessor<Boolean> DATA_IS_CHARGING = SynchedEntityData.defineId(FlyingRangeBasicEntity.class, EntityDataSerializers.BOOLEAN);
    public static final EntityDataAccessor<Integer> STATE = SynchedEntityData.defineId(FlyingRangeBasicEntity.class,
            EntityDataSerializers.INT);
    private int explosionPower = 1;

    public FlyingRangeBasicEntity(EntityType<? extends FlyingMob> p_20806_, Level p_20807_) {
        super(p_20806_, p_20807_);
        this.xpReward = 10;
        this.moveControl = new FlyingRangeBasicMoveControl(this);
        //setAttackingState(0);
    }

    public void setAttackingState(int time) {
        this.entityData.set(STATE, time);
    }
    public int getAttckingState() {
        return this.entityData.get(STATE);
    }

    //注册目标
    protected void registerGoals() {
        this.goalSelector.addGoal(5, new FlyingRangeBasicEntity.RandomFloatAroundGoal(this));
        this.goalSelector.addGoal(7, new FlyingRangeBasicEntity.LookGoal(this));
        this.goalSelector.addGoal(7, new FlyingRangeBasicEntity.ShootFireballGoal(this));
        this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, (p_32755_) -> {
            return Math.abs(p_32755_.getY() - this.getY()) <= 4.0D;
        }));
        this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, 10, true, false, (p_32755_) -> {
            return Math.abs(p_32755_.getY() - this.getY()) <= 4.0D;
        }));
    }
    @Override
    public void registerControllers(AnimationData data) {
        data.addAnimationController(new AnimationController(this, "controller",
                0, this::predicate));
        data.addAnimationController(new AnimationController(this, "controller1",
                0, this::predicate1));
    }
    //该状态播放器控制生物平时的各种动作
    private <E extends IAnimatable> PlayState predicate(AnimationEvent<E> event) {
        //生物在移动，就播放移动的动画，路径和第2步的动作字符串对应
        if (event.isMoving()) {
            event.getController().setAnimation(new AnimationBuilder().addAnimation("animation.model.move", true));
            return PlayState.CONTINUE;
        }

        //不动的话就播放闲置时的动画
        event.getController().setAnimation(new AnimationBuilder().addAnimation("animation.model.idle", true));
        return PlayState.CONTINUE;
    }
    //该状态播放器控制生物攻击时的各种动作
    private <E extends IAnimatable> PlayState predicate1(AnimationEvent<E> event) {
        //如果生物攻击状态为1并且没有死亡，就执行这个攻击动画
        if (this.entityData.get(STATE) == 1 && !(this.dead || this.getHealth() < 0.01 || this.isDeadOrDying())) {
            event.getController().setAnimation(new AnimationBuilder().addAnimation("animation.model.atk", true));
            return PlayState.CONTINUE;
        }
        //播完停止
        return PlayState.STOP;
    }
    private AnimationFactory factory = new AnimationFactory(this);
    @Override
    public AnimationFactory getFactory() {
        // TODO Auto-generated method stub
        return this.factory;
    }

    public boolean isCharging() {
        return this.entityData.get(DATA_IS_CHARGING);
    }

    public void setCharging(boolean p_32759_) {
        this.entityData.set(DATA_IS_CHARGING, p_32759_);
    }

    public int getExplosionPower() {
        return this.explosionPower;
    }

    protected boolean shouldDespawnInPeaceful() {
        return true;
    }

    public boolean hurt(DamageSource p_32730_, float p_32731_) {
        if (this.isInvulnerableTo(p_32730_)) {
            return false;
        } else if (p_32730_.getDirectEntity() instanceof LargeFireball && p_32730_.getEntity() instanceof Player) {
            super.hurt(p_32730_, 1000.0F);
            return true;
        } else {
            return super.hurt(p_32730_, p_32731_);
        }
    }
    @Override
    protected void defineSynchedData() {
        super.defineSynchedData();
        this.entityData.define(DATA_IS_CHARGING, false);
        this.entityData.define(STATE, 0);
    }

    public static AttributeSupplier.Builder createAttributes() {
        return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 10.0D).add(Attributes.FOLLOW_RANGE, 100.0D);
    }

    public SoundSource getSoundSource() {
        return SoundSource.HOSTILE;
    }

    protected SoundEvent getAmbientSound() {
        return SoundEvents.GHAST_AMBIENT;
    }

    protected SoundEvent getHurtSound(DamageSource p_32750_) {
        return SoundEvents.GHAST_HURT;
    }

    protected SoundEvent getDeathSound() {
        return SoundEvents.GHAST_DEATH;
    }

    protected float getSoundVolume() {
        return 5.0F;
    }

    public static boolean checkFlyRangeBasicSpawnRules(EntityType<FlyingRangeBasicEntity> p_32735_, LevelAccessor p_32736_, MobSpawnType p_32737_, BlockPos p_32738_, Random p_32739_) {
        return p_32736_.getDifficulty() != Difficulty.PEACEFUL && p_32739_.nextInt(20) == 0 && checkMobSpawnRules(p_32735_, p_32736_, p_32737_, p_32738_, p_32739_);
    }

    public int getMaxSpawnClusterSize() {
        return 1;
    }

    public void addAdditionalSaveData(CompoundTag p_32744_) {
        super.addAdditionalSaveData(p_32744_);
        p_32744_.putByte("ExplosionPower", (byte)this.explosionPower);
    }

    public void readAdditionalSaveData(CompoundTag p_32733_) {
        super.readAdditionalSaveData(p_32733_);
        if (p_32733_.contains("ExplosionPower", 99)) {
            this.explosionPower = p_32733_.getByte("ExplosionPower");
        }

    }

    static class LookGoal extends Goal {
        private final FlyingRangeBasicEntity flyingRangeBasic;

        public LookGoal(FlyingRangeBasicEntity p_32762_) {
            this.flyingRangeBasic = p_32762_;
            this.setFlags(EnumSet.of(Goal.Flag.LOOK));
        }

        public boolean canUse() {
            return true;
        }

        public boolean requiresUpdateEveryTick() {
            return true;
        }

        public void tick() {
            if (this.flyingRangeBasic.getTarget() == null) {
                Vec3 vec3 = this.flyingRangeBasic.getDeltaMovement();
                this.flyingRangeBasic.setYRot(-((float) Mth.atan2(vec3.x, vec3.z)) * (180F / (float)Math.PI));
                this.flyingRangeBasic.yBodyRot = this.flyingRangeBasic.getYRot();
            } else {
                LivingEntity livingentity = this.flyingRangeBasic.getTarget();
                double d0 = 64.0D;
                if (livingentity.distanceToSqr(this.flyingRangeBasic) < 4096.0D) {
                    double d1 = livingentity.getX() - this.flyingRangeBasic.getX();
                    double d2 = livingentity.getZ() - this.flyingRangeBasic.getZ();
                    this.flyingRangeBasic.setYRot(-((float)Mth.atan2(d1, d2)) * (180F / (float)Math.PI));
                    this.flyingRangeBasic.yBodyRot = this.flyingRangeBasic.getYRot();
                }
            }

        }
    }

    static class FlyingRangeBasicMoveControl extends MoveControl {
        private final FlyingRangeBasicEntity flyingRangeBasicEntity;
        private int floatDuration;

        public FlyingRangeBasicMoveControl(FlyingRangeBasicEntity p_32768_) {
            super(p_32768_);
            this.flyingRangeBasicEntity = p_32768_;
        }

        public void tick() {
            if (this.operation == MoveControl.Operation.MOVE_TO) {
                if (this.floatDuration-- <= 0) {
                    this.floatDuration += this.flyingRangeBasicEntity.getRandom().nextInt(5) + 2;
                    Vec3 vec3 = new Vec3(this.wantedX - this.flyingRangeBasicEntity.getX(), this.wantedY - this.flyingRangeBasicEntity.getY(), this.wantedZ - this.flyingRangeBasicEntity.getZ());
                    double d0 = vec3.length();
                    vec3 = vec3.normalize();
                    if (this.canReach(vec3, Mth.ceil(d0))) {
                        this.flyingRangeBasicEntity.setDeltaMovement(this.flyingRangeBasicEntity.getDeltaMovement().add(vec3.scale(0.1D)));
                    } else {
                        this.operation = MoveControl.Operation.WAIT;
                    }
                }

            }
        }

        private boolean canReach(Vec3 p_32771_, int p_32772_) {
            AABB aabb = this.flyingRangeBasicEntity.getBoundingBox();
            for(int i = 1; i < p_32772_; ++i) {
                aabb = aabb.move(p_32771_);
                if (!this.flyingRangeBasicEntity.level.noCollision(this.flyingRangeBasicEntity, aabb)) {
                    return false;
                }
            }
            return true;
        }
    }

    static class RandomFloatAroundGoal extends Goal {
        private final FlyingRangeBasicEntity flyingRangeBasic;

        public RandomFloatAroundGoal(FlyingRangeBasicEntity p_32783_) {
            this.flyingRangeBasic = p_32783_;
            this.setFlags(EnumSet.of(Goal.Flag.MOVE));
        }

        public boolean canUse() {
            MoveControl movecontrol = this.flyingRangeBasic.getMoveControl();
            if (!movecontrol.hasWanted()) {
                return true;
            } else {
                double d0 = movecontrol.getWantedX() - this.flyingRangeBasic.getX();
                double d1 = movecontrol.getWantedY() - this.flyingRangeBasic.getY();
                double d2 = movecontrol.getWantedZ() - this.flyingRangeBasic.getZ();
                double d3 = d0 * d0 + d1 * d1 + d2 * d2;
                return d3 < 1.0D || d3 > 3600.0D;
            }
        }

        public boolean canContinueToUse() {
            return false;
        }

        public void start() {
            Random random = this.flyingRangeBasic.getRandom();
            double d0 = this.flyingRangeBasic.getX() + (double)((random.nextFloat() * 2.0F - 1.0F) * 16.0F);
            double d1 = this.flyingRangeBasic.getY() + (double)((random.nextFloat() * 2.0F - 1.0F) * 16.0F);
            double d2 = this.flyingRangeBasic.getZ() + (double)((random.nextFloat() * 2.0F - 1.0F) * 16.0F);
            this.flyingRangeBasic.getMoveControl().setWantedPosition(d0, d1, d2, 1.0D);
        }
    }

    static class ShootFireballGoal extends Goal {
        private final FlyingRangeBasicEntity flyingRangeBasic;
        public int chargeTime;

        public ShootFireballGoal(FlyingRangeBasicEntity p_32776_) {
            this.flyingRangeBasic = p_32776_;
        }

        public boolean canUse() {
            flyingRangeBasic.setAttackingState(0);
            return this.flyingRangeBasic.getTarget() != null;
        }

        public void start() {
            this.chargeTime = 0;
        }

        public void stop() {
            this.flyingRangeBasic.setCharging(false);
        }

        public boolean requiresUpdateEveryTick() {
            return true;
        }

        public void tick() {
            LivingEntity livingentity = this.flyingRangeBasic.getTarget();
            if (livingentity != null) {
                double d0 = 64.0D;
                if (livingentity.distanceToSqr(this.flyingRangeBasic) < 4096.0D && this.flyingRangeBasic.hasLineOfSight(livingentity)) {
                    Level level = this.flyingRangeBasic.level;
                    ++this.chargeTime;
                    if (this.chargeTime == 10 && !this.flyingRangeBasic.isSilent()) {
                        level.levelEvent((Player)null, 1015, this.flyingRangeBasic.blockPosition(), 0);
                    }

                    if (this.chargeTime == 20) {
                        double d1 = 4.0D;
                        Vec3 vec3 = this.flyingRangeBasic.getViewVector(1.0F);
                        double d2 = livingentity.getX() - (this.flyingRangeBasic.getX() + vec3.x * 4.0D);
                        double d3 = livingentity.getY(0.5D) - (0.5D + this.flyingRangeBasic.getY(0.5D));
                        double d4 = livingentity.getZ() - (this.flyingRangeBasic.getZ() + vec3.z * 4.0D);
                        if (!this.flyingRangeBasic.isSilent()) {
                            level.levelEvent((Player)null, 1016, this.flyingRangeBasic.blockPosition(), 0);
                        }

                        LargeFireball largefireball = new LargeFireball(level, this.flyingRangeBasic, d2, d3, d4, this.flyingRangeBasic.getExplosionPower());
                        largefireball.setPos(this.flyingRangeBasic.getX() + vec3.x * 4.0D, this.flyingRangeBasic.getY(0.5D) + 0.5D, largefireball.getZ() + vec3.z * 4.0D);
                        level.addFreshEntity(largefireball);
                        this.chargeTime = -40;
                        flyingRangeBasic.setAttackingState(1);
                    }
                } else if (this.chargeTime > 0) {
                    --this.chargeTime;
                }
                else
                {
                    flyingRangeBasic.setAttackingState(0);
                }
                this.flyingRangeBasic.setCharging(this.chargeTime > 10);
            }
        }
    }


}
